import { EventEmitter } from "events";
import { Device } from "mediasoup-client";
import { WebSocketTransport, Peer } from "protoo-client";

export default class Room extends EventEmitter {
  // 构造
  constructor() {
    super();
    this.peer = null;
    this.sendTransport = null;
    this.recvTransport = null;
  }

  // 加入
  join() {
    console.warn("room.join()");
    // 创建 protoo-client WebSocket 连接
    // https://protoo.versatica.com/#protoo
    const wsTransport = new WebSocketTransport('wss://www.habipet.com:9085/?roomId=abc&peerId=123');
    // 一个 protoo-client Peer 代表远程房间的参与者 ...
    this.peer = new Peer(wsTransport);
    // 绑定 Peer 监听事件 ...
    this.peer.on("open", this.onPeerOpen.bind(this));
    this.peer.on("request", this.onPeerRequest.bind(this));
    this.peer.on("notification", this.onPeerNotification.bind(this));
    this.peer.on("failed", console.error);
    this.peer.on("disconnected", console.error);
    this.peer.on("close", console.error);
  }

  // 发送音频
  async sendAudio(track) {
    console.warn("room.sendAudio()");
    // 指示传输将音频轨道发送到 mediasoup router ...
    // https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
    const audioProducer = await this.sendTransport.produce({
      track,
    });
    // 监听 trackended 事件
    // 当正在传输的音频/视频轨道在外部停止时发出。
    // 例如，如果关联的麦克风或网络摄像头源断开连接，则可能会发生这种情况。
    // 这是应用程序关闭/暂停生产者或替换其轨道的好机会。
    // https://mediasoup.org/documentation/v3/mediasoup-client/api/#producer-on-trackended
    audioProducer.on("trackended", async () => {
      console.warn("producer.close() by trackended");
      // 关闭音频生产者
      await this._closeProducer(audioProducer);
    });
    return audioProducer;
  }

  // 发送视频
  async sendVideo(track) {
    console.warn("room.sendVideo()");
    // 指示传输将视频轨道发送到 mediasoup router ...
    // https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-produce
    const videoProducer = await this.sendTransport.produce({
      track,
    });
    // 监听 trackended 事件
    // 当正在传输的音频/视频轨道在外部停止时发出。
    // 例如，如果关联的麦克风或网络摄像头源断开连接，则可能会发生这种情况。
    // 这是应用程序关闭/暂停生产者或替换其轨道的好机会。
    // https://mediasoup.org/documentation/v3/mediasoup-client/api/#producer-on-trackended
    videoProducer.on("trackended", async () => {
      console.warn("producer.close() by trackended");
      // 关闭视频生产者
      await this._closeProducer(videoProducer);
    });
    return videoProducer;
  }

  // protoo-client Peer 连接传输
  async onPeerOpen() {
    console.warn("room.peer:open");
    // 一个设备（表示连接到 mediasoup 路由器以发送和/或接收媒体的端点）
    const device = new Device();
    // 信令（getRouterRtpCapabilities）
    const routerRtpCapabilities = await this.peer
      .request("getRouterRtpCapabilities")
      .catch(console.error);
    // 使用 mediasoup 路由器的 RTP 功能加载设备。
    // 这是设备了解允许的媒体编解码器和其他设置的方式。
    await device.load({ routerRtpCapabilities });
    // 准备（发送/接收）传输通道 ...
    await this._prepareSendTransport(device).catch(console.error);
    await this._prepareRecvTransport(device).catch(console.error);
    // 信令（join）加入房间 ...
    const res = await this.peer.request("join", {
      // 设备 RTP 功能
      // https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-rtpCapabilities
      rtpCapabilities: device.rtpCapabilities,
    });
    // 发送 @open 通知
    this.emit("@open", res);
  }

  // 准备发送传输通道 ...
  async _prepareSendTransport(device) {
    // 信令（createWebRtcTransport）
    const transportInfo = await this.peer
      .request("createWebRtcTransport", {
        producing: true,
        consuming: false,
      })
      .catch(console.error);
    // 创建一个新的 WebRTC 传输来发送媒体。
    // 传输必须事先通过 router.createWebRtcTransport() 在 mediasoup 路由器中创建。
    // transportInfo.iceServers = [{ urls: 'stun:stun.l.google.com:19302' }];
    // https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-createSendTransport
    this.sendTransport = device.createSendTransport(transportInfo);
    // 监听 connect 事件
    this.sendTransport.on(
      "connect",
      ({ dtlsParameters }, callback, errback) => {
        console.warn("room.sendTransport:connect");
        // 信令（connectWebRtcTransport）
        this.peer
          .request("connectWebRtcTransport", {
            transportId: this.sendTransport.id,
            dtlsParameters,
          })
          .then(callback)
          .catch(errback);
      }
    );
    // 监听 produce 事件
    this.sendTransport.on(
      "produce",
      async ({ kind, rtpParameters, appData }, callback, errback) => {
        console.warn("room.sendTransport:produce");
        try {
          // 信令（produce）
          const { id } = await this.peer.request("produce", {
            transportId: this.sendTransport.id,
            kind,
            rtpParameters,
            appData,
          });
          callback({ id });
        } catch (error) {
          errback(error);
        }
      }
    );
  }

  // 准备接收传输通道 ...
  async _prepareRecvTransport(device) {
    // 信令（createWebRtcTransport）
    const transportInfo = await this.peer
      .request("createWebRtcTransport", {
        producing: false,
        consuming: true,
      })
      .catch(console.error);
    // 创建一个新的 WebRTC 传输来接收媒体。
    // 传输必须事先通过 router.createWebRtcTransport() 在 mediasoup 路由器中创建。
    // transportInfo.iceServers = [{ urls: 'stun:stun.l.google.com:19302' }];
    // https://mediasoup.org/documentation/v3/mediasoup-client/api/#device-createRecvTransport
    this.recvTransport = device.createRecvTransport(transportInfo);
    // 监听 connect 事件
    this.recvTransport.on(
      "connect",
      ({ dtlsParameters }, callback, errback) => {
        console.warn("room.recvTransport:connect");
        // 信令（connectWebRtcTransport）
        this.peer
          .request("connectWebRtcTransport", {
            transportId: this.recvTransport.id,
            dtlsParameters,
          })
          .then(callback)
          .catch(errback);
      }
    );
  }

  // 关闭生产者
  async _closeProducer(producer) {
    producer.close();
    // 信令（closeProducer）
    await this.peer
      .request("closeProducer", { producerId: producer.id })
      .catch(console.error);
    // 发送 @producerClosed 通知
    this.emit("@producerClosed", { producerId: producer.id });
  }

  // Peer 信令处理 ...
  onPeerRequest(req, resolve, reject) {
    console.warn("room.peer:request", req.method);
    switch (req.method) {
      case "newConsumer": {
        // 指示传输从 mediasoup router 接收音频或视频轨道 ...
        // https://mediasoup.org/documentation/v3/mediasoup-client/api/#transport-consume
        this.recvTransport
          .consume(req.data)
          .then((consumer) => {
            // 发送 @consumer 通知
            this.emit("@consumer", consumer);
            resolve();
          })
          .catch(reject);
        break;
      }
      default:
        resolve();
    }
  }

  // 处理 Peer Notification ...
  onPeerNotification(notification) {
    console.warn("room.peer:notification", notification);
    // 处理服务器 Peer 的通知, 例如：consumerClosed 加 @ 发送通知 ...
    this.emit("@" + notification.method, notification.data);
  }
}
